﻿using System;
using System.Collections.Generic;
using System.Threading;
using UtilZ.Dotnet.Ex.Base;
using UtilZ.Dotnet.Ex.ZLog;

namespace UtilZ.Dotnet.Ex.Communication.Base
{
    /// <summary>
    /// IClient心跳管理器
    /// </summary>
    internal class ClientHeartManager
    {
        #region 单实例
        private readonly static ClientHeartManager _instance = null;
        public static ClientHeartManager Instance
        {
            get { return _instance; }
        }

        static ClientHeartManager()
        {
            _instance = new ClientHeartManager();
        }
        #endregion


        private readonly object _synRoot = new object();
        private readonly List<HeartDataItem> _list = new List<HeartDataItem>();
        private readonly ThreadEx _heartDataSendThread;

        private ClientHeartManager()
        {
            this._heartDataSendThread = new ThreadEx(this.HeartDataSendThreadMethod, "通信心跳数据发送线程");
        }


        private void HeartDataSendThreadMethod(ThreadPara threadPara)
        {
            try
            {
                int delayMilliseconds;

                while (!threadPara.Token.IsCancellationRequested)
                {
                    try
                    {
                        lock (this._synRoot)
                        {
                            foreach (var item in this._list)
                            {
                                try
                                {
                                    if (item.CheckSendHeartData())
                                    {
                                        item.SendHeartData();
                                    }
                                }
                                catch (Exception e)
                                {
                                    ZLoger.Error(e, "发送心跳数据失败");
                                }
                            }

                            delayMilliseconds = this.GetDelayMilliseconds();
                        }

                        threadPara.WaitOne(delayMilliseconds);
                    }
                    catch (Exception exi)
                    {
                        ZLoger.Error(exi, $"{Thread.CurrentThread.Name}工作异常");
                    }
                }
            }
            catch (Exception ex)
            {
                ZLoger.Error(ex, $"{Thread.CurrentThread.Name}异常停止");
            }
        }

        private int GetDelayMilliseconds()
        {
            long timestamp = TimeEx.GetTimestamp();
            long delayMilliseconds = long.MaxValue, temp;
            foreach (var client in this._list)
            {
                temp = client.GetNextSendHeartWaitMilliseconds(timestamp);
                delayMilliseconds = Math.Min(delayMilliseconds, temp);
            }
            return (int)delayMilliseconds;
        }


        public void Add(IClient client, IClientHeart heartData)
        {
            if (client == null || heartData == null)
            {
                return;
            }

            lock (this._synRoot)
            {
                this._list.Add(new HeartDataItem(client, heartData));
                this._heartDataSendThread.Start();
            }
        }

        public void Remove(IClient client)
        {
            if (client == null)
            {
                return;
            }

            lock (this._synRoot)
            {
                int count = this._list.RemoveAll(t => t.Client == client);
                if (count > 0)
                {
                    if (this._list.Count <= 0)
                    {
                        this._heartDataSendThread.Stop();
                    }
                }
            }
        }




        private class HeartDataItem
        {
            private readonly IClient _client;
            public IClient Client { get { return this._client; } }

            private readonly IClientHeart _heartData;

            private long _lastHeatCheckTimestamp = 0L;


            public HeartDataItem(IClient client, IClientHeart heartData)
            {
                this._client = client;
                this._heartData = heartData;
            }

            public bool CheckSendHeartData()
            {
                var timestamp = TimeEx.GetTimestamp();
                if (timestamp - this._client.LastReceiveDataTimestamp > this._heartData.HeartIntervalMilliseconds &&
                    timestamp - this._client.LastSendDataTimestamp > this._heartData.HeartIntervalMilliseconds &&
                    timestamp - this._lastHeatCheckTimestamp > this._heartData.HeartIntervalMilliseconds)
                {
                    this._lastHeatCheckTimestamp = timestamp;
                    return true;
                }
                else
                {
                    return false;
                }
            }

            public void SendHeartData()
            {
                this._heartData.SendHeartData(this._client);
            }

            public long GetNextSendHeartWaitMilliseconds(long timestamp)
            {
                var lastSendDataTimestamp = this._client.LastSendDataTimestamp;
                if (lastSendDataTimestamp <= 0L)
                {
                    return 0L;
                }

                if (lastSendDataTimestamp <= timestamp)
                {
                    return 0L;
                }
                else
                {
                    return lastSendDataTimestamp - timestamp;
                }
            }

        }
    }



}
